﻿using System;
using System.Text;
using System.IO;
using System.Net.Sockets;
using System.Data;

namespace ServerRPG
{
    class Client
    {
        public TcpClient client;
        public NetworkStream clientStream;

        private bool clientWorking = true;
        public string _x; 
        public string _y;
        public string username = "";
        public string zone;

        public Client(TcpClient client)
        {
            this.client = client;
            Server.AddClient(this);
            clientStream = this.client.GetStream();

            Server.WriteToAll(Messages.SERVER_CLIENTS_ONLINE + "|" + Server.GetClients().ToString());

            Listen();
        }
        /// <summary>
        /// Прослушываем команды клиента.
        /// </summary>
        private void Listen()
        {
            while (clientWorking)
            {
                string message = Read();
                if (!clientWorking) break;
                string remote = client.Client.RemoteEndPoint.ToString();
                switch (CommandParser.GetCommand(message, 0))
                {
                    #region Login
                    case Messages.CLIENT_LOGIN:
                        {
                            string cmessage = CommandParser.GetCommand(message, 1);
                            if (cmessage.Contains(";"))
                            {
                                string login = cmessage.Substring(0, cmessage.IndexOf(';'));
                                string password = cmessage.Substring(cmessage.IndexOf(';') + 1, cmessage.Length - cmessage.IndexOf(';') - 1);
                                if (AccountManager.Find(login, password))
                                {
                                    // Check ban.
                                    if (Convert.ToInt32(DBManager.Select("SELECT level FROM Accounts WHERE username = '" + login + "'").Rows[0][0].ToString()) == -1)
                                    {
                                        Write(Messages.SERVER_CLIENT_LOGIN + "|" + login + "|-1");
                                        break;
                                    }
                                    // Check if account already log in.
                                    bool canLogin = true;
                                    foreach (Client c in Server.GetClientsList())
                                    {
                                        if (login == c.username)
                                        {
                                            Write(Messages.SERVER_CLIENT_LOGIN + "|" + login + "|2");
                                            canLogin = false;
                                            break;
                                        }
                                    }
                                    if (!canLogin) break;
                                    LogManager.WriteExt("\tПодключение: Пользователь '" + login + "' подключился.");
                                    username = login;
                                    Write(Messages.SERVER_CLIENT_LOGIN + "|" + username + "|1");
                                    AccountManager.UpdateLoginDate(login);
                                    // Load 'x' and 'y'
                                    if (Convert.ToInt32(DBManager.Select("SELECT COUNT(*) FROM Characters WHERE username = '" + username + "'").Rows[0][0].ToString()) == 0)
                                    {
                                        _x = "128"; // Start coords.
                                        _y = "128";
                                        zone = "0"; // start zone
                                        DBManager.ExecuteCommand("INSERT INTO Characters(username, x, y, zoneID) VALUES('" + username + "', " + _x + ", " + _y + ", " + zone + ")");
                                    }
                                    else
                                    {
                                        DataTable dt = DBManager.Select("SELECT x, y, zoneID FROM Characters WHERE username = '" + username + "'");
                                        _x = dt.Rows[0][0].ToString();
                                        _y = dt.Rows[0][1].ToString();
                                        zone = dt.Rows[0][2].ToString();
                                    }

                                    Write(Messages.SERVER_CLIENT_ZONE + "|" + username + "|" + zone); // Send Zone
                                    Write(Messages.SERVER_CLIENT_TELE + "|" + username + "|" + _x + "|" + _y); // Send x and y

                                    foreach (Client c in Server.GetClientsList())
                                    {
                                        if (c.username != username)
                                        {
                                            if (c.zone == zone)
                                            {
                                                c.Write(Messages.SERVER_CLIENT_LOGIN + "|" + username);
                                                c.Write(Messages.SERVER_CLIENT_TELE + "|" + username + "|" + _x + "|" + _y);
                                                Write(Messages.SERVER_CLIENT_LOGIN + "|" + c.username);
                                                Write(Messages.SERVER_CLIENT_TELE + "|" + c.username + "|" + c._x + "|" + c._y);
                                            }
                                        }
                                    }
                                    // Send Creatures.
                                    DataTable creaturesDt = DBManager.Select("SELECT creatureID, x, y FROM SpawnPoints WHERE zoneID = " + zone);
                                    if (creaturesDt.Rows.Count > 0)
                                    {
                                        for (int i = 0; i < creaturesDt.Rows.Count; i++)
                                            Write(Messages.SERVER_CREATURE_SPAWN + "|" + creaturesDt.Rows[i][0].ToString() + "|" + 
                                                creaturesDt.Rows[i][1].ToString() + "|" + creaturesDt.Rows[i][2].ToString() + "|");
                                    }
                                }
                                else
                                {
                                    LogManager.WriteExt("\tПодключение: Пользователь '" + login + "' не смог подключился.");
                                    Write(Messages.SERVER_CLIENT_LOGIN + "|" + login + "|0");
                                }
                            }
                            break;
                        }
                    #endregion
                    #region Logout
                    case Messages.CLIENT_LOGOUT:
                        {
                            LogManager.WriteExt("Клиент отключен: " + remote);
                            Server.WriteToAll(Messages.SERVER_CLIENTS_ONLINE + "|" + (Server.GetClients() - 1).ToString());
                            foreach (Client c in Server.GetClientsList())
                            {
                                if (c.username != username)
                                {
                                    c.Write(Messages.SERVER_CLIENT_LOGOUT + "|" + username);
                                }
                            }
                            Close();
                            return;
                        }
                    #endregion
                    case Messages.CLIENT_SERVER_STOP:
                        {
                            if (CommandParser.GetCommand(message, 1) == "1")
                            {
                                if (AccountManager.GetLevel(username) == 4)
                                {
                                    foreach (Client c in Server.GetClientsList())
                                    {
                                        c.Write(Messages.SERVER_CLIENT_LOGOUT + "|" + c.username);
                                    }
                                    Server.Shutdown();
                                }
                            }
                        }
                        break;
                    case Messages.CLIENT_SENDTOALL:
                        {
                            if (CommandParser.GetCommand(message, 1) != string.Empty)
                            {
                                Server.WriteToAll(CommandParser.GetCommand(message, 1));
                            }
                        }
                        break;
                    case Messages.CLIENT_MESSAGE:
                        {
                            //Server.WriteToAll(message);
                            foreach (Client c in Server.GetClientsList())
                            {
                                if (c.zone == zone)
                                    c.Write(message);
                            }
                            break;
                        }
                    // Проверка команд второго уровня.
                    case Messages.CLIENT_MESSAGE_TO_SERVER:
                        {
                            if (CommandParser.GetCommand(message, 1) == username)
                                LogManager.WriteExt("\tIP: " + client.Client.RemoteEndPoint.ToString() +
                                                    "\n\tПользователь: " + username +
                                                    "\n\tСообщение: " + CommandParser.GetCommand(message, 2));
                        }
                        break;
                    // CLIENT_XY|X|Y
                    case Messages.CLIENT_XY:
                        {
                            _x = CommandParser.GetCommand(message, 1);
                            _y = CommandParser.GetCommand(message, 2);
                            DBManager.ExecuteCommand("UPDATE Characters SET x = " + _x + ", y = " + _y + " WHERE username = '" + username + "'");
                            foreach (Client c in Server.GetClientsList())
                            {
                                if (c.zone == zone)
                                    c.Write(Messages.SERVER_CLIENT_TELE + "|" + username + "|" + _x + "|" + _y);
                            }
                            //Server.WriteToAll(Messages.SERVER_CLIENT_TELE + "|" + username + "|" + _x + "|" + _y);
                        }
                        break;
                    case Messages.CLIENT_MOVE:
                        {
                            _x = CommandParser.GetCommand(message, 1);
                            _y = CommandParser.GetCommand(message, 2);
                            //DBManager.ExecuteCommand("UPDATE Characters SET x = " + _x + ", y = " + _y + " WHERE username = '" + username + "'");
                            foreach (Client c in Server.GetClientsList())
                            {
                                if (c.zone == zone)
                                    c.Write(Messages.SERVER_CLIENT_MOVE + "|" + username + "|" + _x + "|" + _y);
                            }
                            //Server.WriteToAll(Messages.SERVER_CLIENT_MOVE + "|" + username + "|" + _x + "|" + _y);
                        }
                        break;
                    case Messages.CLIENT_CLIENTS_ONLINE:
                        {
                            Write(Messages.CLIENT_CLIENTS_ONLINE + "|" + Server.GetClients().ToString());
                        }
                        break;
                    case Messages.CLIENT_ZONE:
                        {
                            foreach (Client c in Server.GetClientsList())
                            {
                                if (c.zone == CommandParser.GetCommand(message, 2) && CommandParser.GetCommand(message, 1) != c.username)
                                {
                                    c.Write(Messages.SERVER_CLIENT_ZONE + "|" + username + "|" + CommandParser.GetCommand(message, 2));

                                    Write(Messages.SERVER_CLIENT_LOGIN + "|" + c.username);
                                    Write(Messages.SERVER_CLIENT_TELE + "|" + c.username + "|" + c._x + "|" + c._y);
                                }
                                if (c.zone == zone && CommandParser.GetCommand(message, 1) != c.username)
                                {
                                    c.Write(Messages.SERVER_CLIENT_ZONE + "|" + username + "|" + CommandParser.GetCommand(message, 2));
                                }
                            }
                            zone = CommandParser.GetCommand(message, 2);
                            DBManager.ExecuteCommand("UPDATE Characters SET zoneID = '" + zone + " ' WHERE username = '" + username + "'"); 
                        }
                        break;
                    default:
                        {
                            LogManager.WriteExt("\tКлиент посылает незвестное сообщение.\n\t\tIP: " +
                                remote + "\n\t\tСообщение: " + message);
                        }
                        break;
                }
            }
        }
        /// <summary>
        /// Принимает сообщение от клиента.
        /// </summary>
        /// <returns>Возвращает принятое сообщение.</returns>
        public string Read()
        {
            int messageCounter = 0;
            int messageBytes = 0;
            string message = "";
            while (clientWorking)
            {
                try
                {
                    string messageByte = clientStream.ReadByte().ToString();
                    if (messageByte != String.Empty)
                    {
                        messageCounter++;

                        if (messageCounter == 1)
                        {
                            // Read bytes.
                            messageBytes = Convert.ToInt32(messageByte);
                        }

                        if (messageCounter >= 3 && messageCounter < 3 + messageBytes - 1)
                        {
                            message += Encoding.GetEncoding(1251).GetString( new Byte[] { Convert.ToByte(messageByte) }, 0, 1);
                        }

                        if (messageCounter == messageBytes + 1)
                        {
                            if (messageBytes > 0)
                            {
                                return message;
                            }
                            messageCounter = 0;
                            message = "";
                        }

                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Close();
                    break;
                }
            }
            return "";
        }
        /// <summary>
        /// Передает клиенту сообщение.
        /// </summary>
        /// <param name="msg">Сообщение</param>
        public void Write(string msg)
        {
            if (clientStream.CanRead && clientStream.CanWrite)
            {
                Byte[] sendBytes = Encoding.GetEncoding(1251).GetBytes("   %" + msg);
                clientStream.Write(sendBytes, 0, sendBytes.Length);
            }
        }
        /// <summary>
        /// Остановка клиента через закрытие CApp.
        /// </summary>
        public void Close()
        {
            clientWorking = false;
            System.Threading.Thread.Sleep(150);
            Server.RemoveClient(this);
            clientStream.Close();
            client.Close();
        }
        /// <summary>
        /// Остановка клиента через закрытие SApp.
        /// </summary>
        public void Stop()
        {
            Write(Messages.SERVER_CLIENT_LOGOUT + "|" + username + "");
            clientWorking = false;
            System.Threading.Thread.Sleep(150);
            clientStream.Close();
            client.Close();
        }
    }
}
